package cn.com.bluemoon.daps.sync.service.impl;

import cn.com.bluemoon.daps.common.enums.SyncToEnv;
import cn.com.bluemoon.daps.common.exception.DapThrowException;
import cn.com.bluemoon.daps.common.toolkit.BmAssetUtils;
import cn.com.bluemoon.daps.model.entity.DapDataModel;
import cn.com.bluemoon.daps.sync.base.CompareResult;
import cn.com.bluemoon.daps.sync.base.DevModelCompareResult;
import cn.com.bluemoon.daps.sync.base.ModifyType;
import cn.com.bluemoon.daps.sync.core.sync.GlobalConfig;
import cn.com.bluemoon.daps.sync.core.sync.TableModel;
import cn.com.bluemoon.daps.sync.core.sync.compare.ModelCompareClient;
import cn.com.bluemoon.daps.sync.feignclient.DapModelModuleFacade;
import cn.com.bluemoon.daps.sync.feignclient.DapSystemModuleFacade;
import cn.com.bluemoon.daps.sync.service.DapDev2ModelSyncService;
import cn.com.bluemoon.daps.sync.service.DapSyncBasicService;
import cn.com.bluemoon.daps.sync.vo.dev2model.DatasourceOut;
import cn.com.bluemoon.daps.sync.vo.dev2model.MetaTableOut;
import cn.com.bluemoon.daps.sync.vo.dev2model.Sync2ModelVo;
import cn.com.bluemoon.daps.system.entity.DapSystemDatasourceEnvironment;
import com.google.common.collect.ImmutableMap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 导入开发库表服务
 * 1.获取开发库元数据
 * 2.获取模型数据
 * 3.进入同步对比器，生成差异结果集
 * 4.入库更新模型数据
 *
 * @author Jarod.Kong
 */
@Service
@Slf4j
public class DapDev2ModelSyncServiceImpl implements DapDev2ModelSyncService {
    @Resource
    private DapSystemModuleFacade systemModuleFacade;

    @Resource
    private DapModelModuleFacade modelModuleFacade;

    @Resource
    private DapSyncBasicService syncBasicService;

    /**
     * 模型id获取对应数据源信息 （转换后）
     *
     * @param modelId 模型id
     * @return 数据源
     */
    @Override
    public DatasourceOut queryDsInfoByModelId(String modelId) {
        GlobalConfig.DataSourceAndEnv dsAndEnv = getDsByModelId(modelId);
        DapSystemDatasourceEnvironment env = dsAndEnv.getEnv();
        DatasourceOut dsOut = new DatasourceOut(env.getHost(), env.getDatabaseName(), env.getPort());
        return dsOut;
    }

    /**
     * 模型id获取对应数据源信息（原生）
     *
     * @param modelId 模型id
     * @return 数据源
     */
    @Override
    public GlobalConfig.DataSourceAndEnv getDsByModelId(String modelId) {
        DapDataModel model = modelModuleFacade.getByIdAsset(modelId, "模型不存在！！");
        GlobalConfig.DataSourceAndEnv dataSourceAndEnv = systemModuleFacade.getSysDataSourceAndEnvByIdAndEnv(model.getDataSourceId(), SyncToEnv.MODEL);
        return dataSourceAndEnv;
    }

    /**
     * 模型id获取数据源库表列表
     *
     * @param modelId 模id
     * @return 库表列表
     */
    @Override
    public List<MetaTableOut> queryDsTableInfoByModelId(String modelId) {
        GlobalConfig globalConfig = syncBasicService.buildSyncGlobalConfig(modelId, SyncToEnv.MODEL, null, true);
        List<TableModel> metadataTableModel = syncBasicService.getMetadataTableModel(globalConfig);
        List<MetaTableOut> tables = metadataTableModel.stream().map(TableModel::getTable)
                .map(t -> {
                    MetaTableOut m = new MetaTableOut();
                    m.setId(t.getEngName());
                    m.setName(t.getEngName());
                    m.setNameZh(t.getName());
                    return m;
                }).collect(Collectors.toList());
        log.info("获取开发库表：{}", tables.size());
        return tables;
    }

    /**
     * 开发库导入模型-对比结果 + 基于所选的开发库表ok （部分库表vs模型表）
     * 所选的库表对比
     *
     * @param modelId      模型id
     * @param tableNameIds 所选库表名  不是表id
     * @return 结果集
     */
    @Override
    public List<CompareResult<DevModelCompareResult>> compareDevAndModelTables(String modelId, List<String> tableNameIds) {
        BmAssetUtils.isTrue(tableNameIds != null && !tableNameIds.isEmpty(), "导入开发库表中所选表不可为空！！");
        return dev2ModelTablesCompareAndInsert(modelId, SyncToEnv.MODEL, tableNameIds, false);
    }

    /**
     * 开发库导入模型-对比结果 + 基于所选的开发库表ok （部分库表vs模型表）
     * 所选的库表对比
     * +返回表变化报告
     *
     * @param modelId      模型id
     * @param tableNameIds 所选库表名  不是表id
     * @return 结果集
     */
    @Override
    public Tuple2<Map<ModifyType, Integer>, List<CompareResult<DevModelCompareResult>>> compareDevAndModelTablesAndAnalyse(String modelId, List<String> tableNameIds) {
        BmAssetUtils.isTrue(tableNameIds != null && !tableNameIds.isEmpty(), "导入开发库表中所选表不可为空！！");
        return dev2ModelTablesCompareAndInsert0(modelId, SyncToEnv.MODEL, tableNameIds, false, true);
    }

    /**
     * 开发库导入模型-对比结果 + 基于所选的开发库表+ 入库 （部分库表vs模型表）
     * 所选的库表对比
     *
     * @param modelId      模型id
     * @param tableNameIds 所选库表名
     * @return 结果集
     */
    @Override
    public List<CompareResult<DevModelCompareResult>> compareDevAndModelTables2DB(String modelId, List<String> tableNameIds, String selectedGroupId) {
        BmAssetUtils.isTrue(tableNameIds != null && !tableNameIds.isEmpty(), "导入开发库表中所选表不可为空！！");
        return dev2ModelTablesCompareAndInsert(modelId, SyncToEnv.MODEL, tableNameIds, true, selectedGroupId);
    }

    @Override
    public List<CompareResult<DevModelCompareResult>> compareDevAndModelTables2DB(Sync2ModelVo vo) {
        return compareDevAndModelTables2DB(vo.getModelId(), vo.getTableNames(), vo.getGroupId());
    }

    /**
     * 根据模型id进行
     * 开发库导入模型-对比结果 + 基于所选的开发库表+ 入库 （全库表vs模型表）
     * 全库表对比
     *
     * @param modelId 模型id
     * @return 结果集
     */
    @Override
    public List<CompareResult<DevModelCompareResult>> compareDevAndModelTables2DB(String modelId) {
        return dev2ModelTablesCompareAndInsert(modelId, SyncToEnv.MODEL, Collections.emptyList(), true);
    }

    /**
     * 开发库导入模型-对比结果 + 基于所选的开发库表ok （支持全库表vs模型表）
     *
     * @param modelId         模型id
     * @param syncToEnv       同步到
     * @param tableNameIds    所选库表名s 不是表id 若为空，则全库表与模型全表做对比和入库动作
     * @param res2DbSink      是否吧对比结果入库
     * @param analyseTbReport 分析表变化报告
     * @return 结果集
     */
    private Tuple2<Map<ModifyType, Integer>, List<CompareResult<DevModelCompareResult>>> dev2ModelTablesCompareAndInsert0(String modelId,
                                                                                                                          SyncToEnv syncToEnv,
                                                                                                                          List<String> tableNameIds,
                                                                                                                          boolean res2DbSink,
                                                                                                                          boolean analyseTbReport) {
        return dev2ModelTablesCompareAndInsert0(modelId, syncToEnv, tableNameIds, res2DbSink, analyseTbReport, null);
    }

    /**
     * 开发库导入模型-对比结果 + 基于所选的开发库表ok （支持全库表vs模型表）
     *
     * @param modelId         模型id
     * @param syncToEnv       同步到
     * @param tableNameIds    所选库表名s 不是表id 若为空，则全库表与模型全表做对比和入库动作
     * @param res2DbSink      是否吧对比结果入库
     * @param analyseTbReport 分析表变化报告
     * @param selectedGroupId 指定导入到哪个组
     * @return 结果集
     */
    private Tuple2<Map<ModifyType, Integer>, List<CompareResult<DevModelCompareResult>>> dev2ModelTablesCompareAndInsert0(String modelId,
                                                                                                                          SyncToEnv syncToEnv,
                                                                                                                          List<String> tableNameIds,
                                                                                                                          boolean res2DbSink,
                                                                                                                          boolean analyseTbReport,
                                                                                                                          String selectedGroupId) {
        if (syncToEnv == null) {
            syncToEnv = SyncToEnv.MODEL;
        }
        GlobalConfig globalConfig = syncBasicService.buildSyncGlobalConfig(modelId, syncToEnv, null, true, selectedGroupId);
        if (tableNameIds == null || tableNameIds.isEmpty()) {
            log.warn("入参tableNameIds为空，不进行全库表与模型全表做对比和入库动作！");
            throw new DapThrowException("请选择相关表进行同步");
        }
        globalConfig.setTableIdentity(new GlobalConfig.TableIdentity(null, tableNameIds));
        globalConfig.setSyncToEnv(SyncToEnv.MODEL);
        Tuple2<List<TableModel>, List<TableModel>> localAndMetadataTableModel = syncBasicService.getLocalAndMetadataTableModel(globalConfig);
        List<TableModel> modelV2 = localAndMetadataTableModel.getT2();
        List<TableModel> modelV1 = localAndMetadataTableModel.getT1().stream().filter(t->tableNameIds.contains(t.getTable().getEngName())).collect(Collectors.toList());
        globalConfig.setAddSink(res2DbSink);
        log.info("元数据库表vs模型库表数量：{} vs {}", modelV2.size(), modelV1.size());
        // v1是模型（旧） v2是元数据（新）
        List<Map<ModifyType.ModifyLevel, List<CompareResult<DevModelCompareResult>>>> res3 =
                ModelCompareClient.doCompare(ModelCompareClient.BizType.Dev2Model, globalConfig, modelV1, modelV2);
        // 去掉忽略的数据 然后返回给前端
        Map<ModifyType, Integer> tableCompareResult = new HashMap<>();
        if (analyseTbReport) {
            tableCompareResult = analyseTableCompareResult(res3);
        }
        List<CompareResult<DevModelCompareResult>> res = getBizCompareResults(res3);
        log.info("导入开发库（对比结构）差异集：size:{}", res.size());
        return Tuples.of(tableCompareResult, res);
    }

    private List<CompareResult<DevModelCompareResult>> dev2ModelTablesCompareAndInsert(String modelId,
                                                                                       SyncToEnv syncToEnv,
                                                                                       List<String> tableNameIds,
                                                                                       boolean res2DbSink) {
        return dev2ModelTablesCompareAndInsert(modelId, syncToEnv, tableNameIds, res2DbSink, null);
    }

    /**
     * 开发库导入模型-对比结果 + 基于所选的开发库表ok （支持全库表vs模型表）
     *
     * @param modelId      模型id
     * @param syncToEnv    同步到
     * @param tableNameIds 所选库表名s 不是表id 若为空，则全库表与模型全表做对比和入库动作
     * @param res2DbSink   是否吧对比结果入库
     * @return 结果集
     */
    @SuppressWarnings("ConstantConditions")
    private List<CompareResult<DevModelCompareResult>> dev2ModelTablesCompareAndInsert(String modelId,
                                                                                       SyncToEnv syncToEnv,
                                                                                       List<String> tableNameIds,
                                                                                       boolean res2DbSink,
                                                                                       String selectedGroupId) {
        return Optional
                .ofNullable(dev2ModelTablesCompareAndInsert0(modelId, syncToEnv, tableNameIds, res2DbSink, false, selectedGroupId))
                .orElse(Tuples.of(Collections.emptyMap(), Collections.emptyList()))
                .getT2();
    }

    /**
     * 分析结果集的表变化信息
     *
     * @param res3 结果集
     * @return 变化
     */
    private Map<ModifyType, Integer> analyseTableCompareResult(List<Map<ModifyType.ModifyLevel, List<CompareResult<DevModelCompareResult>>>> res3) {
        int create = 0, drop = 0, alter = 0;
        for (Map<ModifyType.ModifyLevel, List<CompareResult<DevModelCompareResult>>> map : res3) {
            // 如果是表级别且不是忽略的结果
            List<CompareResult<DevModelCompareResult>> list = map.getOrDefault(ModifyType.ModifyLevel.TABLE, Collections.emptyList());
            if (!list.isEmpty()) {
                List<CompareResult<DevModelCompareResult>> noIgnoreList = list.stream().filter(d -> !DevModelCompareResult.Action.Ignore.equals(d.getData().getAction())).collect(Collectors.toList());
                if (!noIgnoreList.isEmpty()) {
                    boolean isDrop = list.stream().allMatch(c -> c.getModifyType().equals(ModifyType.DROP));
                    boolean isCreate = list.stream().allMatch(c -> c.getModifyType().equals(ModifyType.CREATE));
                    if (isDrop) {
                        drop++;
                    } else if (isCreate) {
                        create++;
                    } else {
                        alter++;
                    }
                }
            } else {
                alter++;
            }
        }
        return ImmutableMap.of(ModifyType.CREATE, create, ModifyType.ALTER, alter, ModifyType.DROP, drop);
    }

    /**
     * 只保留业务需要的变更数据
     *
     * @param res3 统一对比结果集
     * @return list
     */
    private List<CompareResult<DevModelCompareResult>> getBizCompareResults(List<Map<ModifyType.ModifyLevel, List<CompareResult<DevModelCompareResult>>>> res3) {
        List<CompareResult<DevModelCompareResult>> res = new ArrayList<>();
        for (Map<ModifyType.ModifyLevel, List<CompareResult<DevModelCompareResult>>> map : res3) {
            for (Map.Entry<ModifyType.ModifyLevel, List<CompareResult<DevModelCompareResult>>> entry : map.entrySet()) {
                List<CompareResult<DevModelCompareResult>> compareResults = entry.getValue().stream().filter(d -> !DevModelCompareResult.Action.Ignore.equals(d.getData().getAction())).collect(Collectors.toList());
                res.addAll(compareResults);
            }
        }
        // 2022/4/15 排序
        return res.stream()
                .sorted(Comparator
                        .comparing((Function<CompareResult<DevModelCompareResult>, String>) CompareResult::getTableName)
                        .thenComparing(o -> o.getModifyType().getRank()))
                .collect(Collectors.toList());
    }

    /**
     * 根据模型id进行
     * 开发库导入模型-对比结果 + 基于所选的开发库表+ 不入库 （全库表vs模型表）
     * 全库表对比
     *
     * @param modelId 模型id
     * @return 结果集
     */
    @Override
    public List<CompareResult<DevModelCompareResult>> compareDevAndModelTables(String modelId) {
        return dev2ModelTablesCompareAndInsert(modelId, SyncToEnv.MODEL, Collections.emptyList(), false);
    }
}
